export function patch(oldVnode, vnode) {
  if (!oldVnode) {
    return createElm(vnode)
  } else {
    const isRealElement = oldVnode.nodeType
    if (isRealElement) {
      const oldElm = oldVnode
      const parentElm = oldElm.parentNode
      let el = createElm(vnode)
      parentElm.insertBefore(el, oldElm.nextSibling)
      parentElm.removeChild(oldVnode)
      return el
    } else {
      // oldVnode 是虚拟dom update过程，使用diff算法
      // 不同的标签，直接替换
      if (oldVnode.tag !== vnode.tag) {
        oldVnode.el.parentNode.replaceChild(createElm(vnode), oldVnode.el)
      }
      // 文本节点
      if (!oldVnode.tag) {
        if (oldVnode.text !== vnode.text) {
          oldVnode.el.textContent = vnode.text
        }
      }
      // 标签一致，且不是文本节点
      const el = (vnode.el = oldVnode.el)
      // 更新标签的属性
      updateProperties(vnode, oldVnode.data)
      const oldCh = oldVnode.children || []
      const newCh = vnode.children || []
      if (oldCh.length > 0 && newCh.length > 0) {
        // 都有子节点
        updateChildren(el, oldCh, newCh)
      } else if (oldCh.length) {
        el.innerHTML = ''
      } else if (newCh.length) {
        for (let child of newCh) {
          el.appendChild(createElm(child))
        }
      }
    }
  }
}

function createComponent(vnode) {
  let i = vnode.data
  if ((i = i.hook) && (i = i.init)) {
    i(vnode)
  }
  if (vnode.componentInstance) {
    return true
  }
}

function createElm(vnode) {
  let { tag, data, key, children, text } = vnode
  if (typeof tag === 'string') {
    if (createComponent(vnode)) {
      return vnode.componentInstance.$el
    }
    vnode.el = document.createElement(tag)
    updateProperties(vnode)
    children.forEach((child) => {
      return vnode.el.appendChild(createElm(child))
    })
  } else {
    vnode.el = document.createTextNode(text)
  }
  return vnode.el
}

function updateProperties(vnode, oldProps = {}) {
  let newProps = vnode.data || {}
  let el = vnode.el

  // 移除消失的属性
  for (let key of Object.keys(oldProps)) {
    if (!newProps[key]) {
      el.removeAttribute(key)
    }
  }
  // style 属性 特殊处理
  const newStyle = newProps.style || {}
  const oldStyle = oldProps.style || {}
  for (const key of Object.keys(oldStyle)) {
    if (!newStyle[key]) {
      el.style[key] = ''
    }
  }

  // 这里是 新属性的添加 上面是旧属性的删除
  for (let key of Object.keys(newProps)) {
    if (key === 'style') {
      for (let styleName of Object.keys(newProps[key])) {
        el[key][styleName] = newProps[key][styleName]
      }
    } else if (key === 'class') {
      el.className = newProps[key]
    } else {
      el.setAttribute(key, newProps[key])
    }
  }
}

function isSameVnode(oldVnode, newVnode) {
  return oldVnode.tag === newVnode.tag && oldVnode.key === newVnode.key
}
// diff 核心
function updateChildren(parent, oldCh, newCh) {
  let oldStartIndex = 0
  let oldStartVnode = oldCh[oldStartIndex]
  let oldEndIndex = oldCh.length - 1
  let oldEndVnode = oldCh[oldEndIndex]

  let newStartIndex = 0
  let newStartVnode = newCh[newStartIndex]
  let newEndIndex = newCh.length - 1
  let newEndVnode = newCh[newEndIndex]

  function makeIndexByKey(children) {
    let map = {}
    children.forEach((child, index) => {
      map[child.key] = index
    })

    return map
  }

  let map = makeIndexByKey(oldCh)

  while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
    if (!oldStartVnode) {
      oldStartIndex += 1
      oldStartVnode = oldCh[oldStartIndex]
    } else if (!oldEndVnode) {
      oldEndIndex -= 1
      oldEndVnode = oldCh[oldEndIndex]
    } else if (isSameVnode(oldStartVnode, newStartVnode)) {
      // 头头相等
      // 递归比较子节点
      patch(oldStartVnode, newStartVnode)
      oldStartIndex += 1
      newStartIndex += 1
      oldStartVnode = oldCh[oldStartIndex]
      newStartVnode = newCh[newStartIndex]
    } else if (isSameVnode(oldEndVnode, newEndVnode)) {
      // 尾尾相等
      patch(oldEndVnode, newEndVnode)
      oldEndIndex -= 1
      newEndIndex -= 1
      oldEndVnode = oldCh[oldEndIndex]
      newEndVnode = newCh[newEndIndex]
    } else if (isSameVnode(oldStartVnode, newEndVnode)) {
      // 旧头新尾相等
      patch(oldStartVnode, newEndVnode)
      // 移动旧头到尾部
      parent.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling)
      oldStartIndex += 1
      newEndIndex -= 1
      oldStartVnode = oldCh[oldStartIndex]
      newEndVnode = newCh[newEndIndex]
    } else if (isSameVnode(oldEndVnode, newStartVnode)) {
      // 旧尾新头相等
      patch(oldEndVnode, newStartVnode)
      parent.insertBefore(oldEndVnode.el, oldStartVnode.el)
      oldEndIndex -= 1
      newStartIndex += 1
      oldEndVnode = oldCh[oldEndIndex]
      newStartVnode = newCh[newStartIndex]
    } else {
      // 都不满足
      // 根据旧节点的 key index 映射表，从新开始节点进行查找，如果找到就进行移动，找不到直接插入
      let moveIndex = map[newStartVnode.key]
      if (!moveIndex) {
        parent.insertBefore(createElm(newStartVnode), oldStartVnode.el)
      } else {
        let moveVnode = oldCh[moveIndex]
        oldCh[moveIndex] = void 0
        parent.insertBefore(moveVnode.el, oldStartVnode.el)
        patch(moveVnode, newStartVnode)
      }
    }
  }

  // 旧节点循环完毕，还有新节点没遍历，说明新节点新增了，要添加到头部或者尾部
  if (newStartIndex <= newEndIndex) {
    for (let i = newStartIndex; i <= newEndIndex; i++) {
      const ele =
        newCh[newEndIndex + 1] === null ? null : newCh[newEndIndex + 1].el
      parent.insertBefore(createElm(newCh[i]), ele)
    }
  }

  // 新节点循环完毕，还有旧节点没有遍历，要删除旧节点
  if (oldStartIndex <= oldEndIndex) {
    for (let i = oldStartIndex; i <= oldEndIndex; i++) {
      let child = oldCh[i]
      if (child !== void 0) {
        parent.removeChild(child.el)
      }
    }
  }
}
